let colorTF = [];
let opacityTF = [];

//let dataRange = [0, 1]; // 
let size = 500;
let svg = null;

// Initialize the d3 objects
function initializeTFunc() {
  svg = d3.select("#tfunc")
    .append("svg")
    .attr("width", size)
    .attr("height", size);

  updateTFunc();
}

function initializeOpacityTF() {
  opacityTF = colorTF.map(d => [d[0], 1]); // Default to full opacity
}

let xScale, yScale;
let selectedOpacityPointIndex = null;

// Initialize scales for mapping data to screen space
function initializeScales() {
  xScale = d3.scaleLinear()
             .domain(dataRange)
             .range([0, size]);
  
  yScale = d3.scaleLinear()
             .domain([0, 1])
             .range([size, 0]);
}
initializeOpacityTF();
// Update the transfer function editor
function updateTFunc() {
  svg.selectAll("*").remove(); // Clear existing content

  // Draw colorTF as a color bar
  const barWidth = size / colorTF.length;
  svg.selectAll("rect")
    .data(colorTF)
    .enter().append("rect")
      .attr("x", (d, i) => i * barWidth)
      .attr("y", 0)
      .attr("width", barWidth)
      .attr("height", 20)
      .style("fill", d => d[1].toString());

  // Draw the opacity transfer function
  let line = d3.line()
               .x(d => xScale(d[0]))
               .y(d => yScale(d[1]));

  svg.append("path")
     .datum(opacityTF)
     .attr("fill", "none")
     .attr("stroke", "black")
     .attr("stroke-width", 2)
     .attr("d", line);

  svg.selectAll("circle")
     .data(opacityTF)
     .enter().append("circle")
       .attr("cx", d => xScale(d[0]))
       .attr("cy", d => yScale(d[1]))
       .attr("r", 5)
       .style("cursor", "pointer")
       .style("fill", "blue")
       .call(d3.drag()
         .on("start", opacityPointDragStarted)
         .on("drag", opacityPointDragged)
         .on("end", opacityPointDragEnded));
}

function opacityPointDragStarted(event, d) {
  selectedOpacityPointIndex = opacityTF.indexOf(d);
}

function opacityPointDragged(event, d) {
  let newX = xScale.invert(event.x);
  let newY = yScale.invert(event.y);

  // Constrain newY within the range [0, 1]
  newY = Math.max(0, Math.min(newY, 1));

  // Constraint checks for end points
  if (selectedOpacityPointIndex === 0 || selectedOpacityPointIndex === opacityTF.length - 1) {
    // Only allow vertical movement for end points
    opacityTF[selectedOpacityPointIndex] = [d[0], newY];
  } else {
    // For interior points, prevent crossing adjacent points and constrain within the range
    const leftBound = opacityTF[selectedOpacityPointIndex - 1][0];
    const rightBound = opacityTF[selectedOpacityPointIndex + 1][0];
    newX = Math.max(leftBound, Math.min(newX, rightBound));
    
    opacityTF[selectedOpacityPointIndex] = [newX, newY];
  }

  updateTFunc();
  updateVR(colorTF, opacityTF, false); // Update the volume renderer
}


function opacityPointDragEnded() {
  selectedOpacityPointIndex = null;
}

// Function to process the upload
function upload() {
  if (input.files.length > 0) {
    let file = input.files[0];
    let fReader = new FileReader();
    fReader.readAsArrayBuffer(file);

    fReader.onload = function(e) {
      let fileData = fReader.result;

      // Load the .vti data and initialize volren
      initializeVR(fileData, function() {
        resetTFs();
        updateTFunc();
        updateVR(colorTF, opacityTF, false);
      });
    }
  }
}


// Attach upload process to the loadData button
var input = document.getElementById("loadData");
input.addEventListener("change", upload);

function resetTFs() {
  makeSequential();
  makeOpacity();
}

// Make a default opacity TF
function makeOpacity() {
  opacityTF = [
    [dataRange[0], 0],
    [dataRange[1], 1]
  ];
}

// Make a sequential color TF
function makeSequential() {
  const colorScale = d3.scaleSequential(d3.interpolateBlues)
                      .domain(dataRange);
  const numPoints = 5;
  colorTF = Array.from({ length: numPoints }, (_, i) => {
    const t = dataRange[0] + (dataRange[1] - dataRange[0]) * i / (numPoints - 1);
    return [t, d3.rgb(colorScale(t))];
  });
}

// Make a diverging color TF
function makeDiverging() {
  const colorScale = d3.scaleDiverging(d3.interpolateCool)
                      .domain([dataRange[0], (dataRange[0] + dataRange[1]) / 2, dataRange[1]]);
  const numPoints = 5;
  colorTF = Array.from({ length: numPoints }, (_, i) => {
    const t = dataRange[0] + (dataRange[1] - dataRange[0]) * i / (numPoints - 1);
    return [t, d3.rgb(colorScale(t))];
  });
}

// Make a categorical color TF
function makeCategorical() {
  const colors = [d3.rgb(255, 0, 0), d3.rgb(0, 255, 0), d3.rgb(0, 0, 255)];
  const numPoints = colors.length;
  colorTF = Array.from({ length: numPoints }, (_, i) => {
    const t = dataRange[0] + (dataRange[1] - dataRange[0]) * i / (numPoints - 1);
    return [t, colors[i]];
  });
}

// Initial setup
initializeScales();
resetTFs();
initializeTFunc();

document.getElementById('sequentialButton').addEventListener('click', function() {
    makeSequential();
    updateTFunc();
    updateVR(colorTF, opacityTF, false);
});

document.getElementById('divergingButton').addEventListener('click', function() {
    makeDiverging();
    updateTFunc();
    updateVR(colorTF, opacityTF, false);
});

document.getElementById('categoricalButton').addEventListener('click', function() {
    makeCategorical();
    updateTFunc();
    updateVR(colorTF, opacityTF, true);
});
